using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using System.Windows.Shapes;

namespace Charting
{
    class BoundingPlanes
    {
        Model3DGroup modelGroup;
        TextBlock tipTextBlock;

        internal BoundingPlanes(ChartData chartData)
        {
            this.modelGroup = new Model3DGroup();

            Point3D topLeft = new Point3D(-1, 1, 1);
            Point3D topBack = new Point3D(-1, 1, -1);
            Point3D topRight = new Point3D(1, 1, -1);
            Point3D bottomLeft = new Point3D(-1, -1, 1);
            Point3D bottomBack = new Point3D(-1, -1, -1);
            Point3D bottomRight = new Point3D(1, -1, -1);
            Point3D bottomFront = new Point3D(1, -1, 1);

            Point3D labelTopLeft = new Point3D(-1, 1, 3);
            Point3D labelTopRight = new Point3D(3, 1, -1);
            Point3D labelBottomLeft = new Point3D(-1, -1, 3);
            Point3D labelBottomFrontLeft = new Point3D(1, -1, 3);
            Point3D labelBottomFrontRight = new Point3D(3, -1, 1);
            Point3D labelBottomRight = new Point3D(3, -1, -1);

            this.AddRectangle(bottomLeft, bottomBack, topLeft, topBack, false,
                this.CreateLinedVisual(chartData.RangeLabels.Count));
            this.AddRectangle(bottomBack, bottomRight, topBack, topRight, false,
                this.CreateLinedVisual(chartData.RangeLabels.Count));
            this.AddRectangle(bottomFront, bottomRight, bottomLeft, bottomBack, false,
                this.CreateLinedVisual(chartData.Columns.Count));

            this.AddRectangle(labelBottomLeft, bottomLeft, labelTopLeft, topLeft, true,
                this.CreateLabeledVisual(HorizontalAlignment.Right, chartData.RangeLabels, null));

            this.AddRectangle(bottomRight, labelBottomRight, topRight, labelTopRight, true,
                this.CreateLabeledVisual(HorizontalAlignment.Left, chartData.RangeLabels, null));

            this.AddRectangle(labelBottomFrontLeft, bottomFront, labelBottomLeft, bottomLeft, true,
                this.CreateLabeledVisual(HorizontalAlignment.Right, chartData.Columns, null));

            this.AddRectangle(
                bottomFront, labelBottomFrontRight, bottomRight, labelBottomRight, true,
                this.CreateLabeledVisual(HorizontalAlignment.Left,
                    chartData.Rows, chartData.GetRowLabelBrushes()));

            if (chartData.Title != null)
            {
                Point3D titleLeft = new Point3D(-1, 3, 1);
                Point3D titleBack = new Point3D(-1, 3, -1);

                this.AddRectangle(topLeft, topBack, titleLeft, titleBack, true,
                    this.CreateTitleVisual(chartData.Title, Brushes.Purple));
            }

            Point3D tipBack = new Point3D(-1, 3, -1);
            Point3D tipRight = new Point3D(1, 3, -1);

            this.AddRectangle(topBack, topRight, tipBack, tipRight, true,
                this.CreateTipVisual());
        }

        internal Model3D Model { get { return this.modelGroup; } }

        internal string Tip
        {
            get { return this.tipTextBlock.Text; }
            set { this.tipTextBlock.Text = value; }
        }

        void AddRectangle(Point3D p1, Point3D p2, Point3D p3, Point3D p4,
            bool isEmissive, Visual visual)
        {
            GeometryModel3D geometryModel = new GeometryModel3D();
            geometryModel.Material = new DiffuseMaterial(new VisualBrush(visual));
            this.modelGroup.Children.Add(geometryModel);

            MeshGeometry3D meshGeometry = new MeshGeometry3D
            {
                Positions = new Point3DCollection { p1, p2, p3, p4 },

                TextureCoordinates = new PointCollection {
                    new Point(0, 1), new Point(1, 1), new Point(0, 0), new Point(1, 0) },

                TriangleIndices = new Int32Collection { 0, 1, 2, 2, 1, 3 }
            };
            geometryModel.Geometry = meshGeometry;

        }

        Grid CreateLinedVisual(int lineCount)
        {
            var solidColorBrush = new SolidColorBrush(Color.FromArgb(92, 64, 64, 64));

            var grid = this.CreateLayoutGrid(lineCount, solidColorBrush);

            for (int i = 0; i < lineCount; i++)
            {
                var rectangle = new Rectangle
                {
                    Fill = new SolidColorBrush(Color.FromArgb(92, 0, 0, 0)),
                    Width = 1,
                    Height = 0.01,
                    HorizontalAlignment = HorizontalAlignment.Center,
                    VerticalAlignment = VerticalAlignment.Center
                };
                Grid.SetRow(rectangle, i + 1);
                grid.Children.Add(rectangle);
            }

            return grid;
        }

        Grid CreateLabeledVisual(
            HorizontalAlignment textAlignment, IList<string> labels, IList<Brush> labelBrushes)
        {
            var grid = this.CreateLayoutGrid(labels.Count, Brushes.Transparent);

            for (int i = 0; i < labels.Count; i++)
            {
                var textBlock = new TextBlock
                {
                    Text = labels[i],
                    FontSize = 0.08,
                    Foreground = labelBrushes == null ? Brushes.Black : labelBrushes[i],
                    Margin = new Thickness(0.02),
                    HorizontalAlignment = textAlignment,
                    VerticalAlignment = VerticalAlignment.Center
                };
                Grid.SetRow(textBlock, i + 1);
                grid.Children.Add(textBlock);
            }

            return grid;
        }

        Grid CreateTitleVisual(string title, Brush brush)
        {
            var grid = new Grid { Width = 1, Height = 1, Background = Brushes.Transparent };

            var textBlock = new TextBlock
            {
                Text = title,
                FontSize = 0.12,
                Foreground = brush,
                Margin = new Thickness(0.02),
                HorizontalAlignment = HorizontalAlignment.Center,
                VerticalAlignment = VerticalAlignment.Bottom
            };
            grid.Children.Add(textBlock);

            return grid;
        }

        Grid CreateTipVisual()
        {
            var grid = new Grid { Width = 1, Height = 1, Background = Brushes.Transparent };

            this.tipTextBlock = new TextBlock
            {
                Text = "",
                FontSize = 0.08,
                Foreground = Brushes.Black,
                Margin = new Thickness(0.02),
                HorizontalAlignment = HorizontalAlignment.Center,
                VerticalAlignment = VerticalAlignment.Bottom
            };
            grid.Children.Add(this.tipTextBlock);

            return grid;
        }

        Grid CreateLayoutGrid(int itemCount, Brush brush)
        {
            var grid = new Grid { Width = 1, Height = 1, Background = brush };

            for (int i = 0; i < itemCount + 2; i ++)
            {
                double proportion = i == 0 || i == itemCount + 1 ? 1 : 2;

                RowDefinition rowDefinition = new RowDefinition {
                    Height = new GridLength(proportion, GridUnitType.Star)};
                grid.RowDefinitions.Add(rowDefinition);
            }

            return grid;
        }
    }
}
